using System;
using Net.Common;
using System.Collections.Generic;
#if RECAST_NATIVE
using Net.AI.Native;
using static Net.AI.Native.RecastDll;
#else
using Recast;
#endif

namespace Net.AI
{
    [Serializable]
    public class AgentEntity
    {
        public float speed = 5f;
        public float agentHeight = 1f;
        public float angularSpeed = 0.25f;
        public EntityTransform transform;
        private readonly List<Vector3> pathPoints = new();
        public FindPathMode findPathMode;
        public dtStraightPathOptions m_straightPathOptions = dtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS;
        public NavmeshSystem System { get; set; }

        public float RemainingDistance => pathPoints.Count > 0 ? Vector3.Distance(transform.Position, pathPoints[0]) : 0f;
        public Vector3 Position => transform.Position;
        public Quaternion Rotation => transform.Rotation;
        public Vector3 Destination => pathPoints.Count > 0 ? pathPoints[0] : Position;
        public Vector3 NextPosition => pathPoints.Count > 0 ? pathPoints[pathPoints.Count - 1] : Position;
        public bool IsStop => pathPoints.Count <= 0;
        public Vector3 Velocity { get; set; }

        public Action OnFinish { get; set; }
        public Action<Vector3> OnNextPath { get; set; }

        public AgentEntity() : this(null, new()) { }

        public AgentEntity(NavmeshSystem navmeshSystem) : this(navmeshSystem, new()) { }

        public AgentEntity(NavmeshSystem navmeshSystem, EntityTransform transform)
        {
            System = navmeshSystem;
            this.transform = transform;
        }

        public void SetPositionAndRotation(Vector3 position, Quaternion rotation)
        {
            transform.Position = position;
            transform.Rotation = rotation;
        }

        public void OnUpdate(float dt)
        {
            if (Velocity != Vector3.zero)
            {
                var targetPos = transform.Position + Velocity;
                SetDestination(targetPos);
                Velocity = Vector3.zero;
            }
            if (pathPoints.Count > 0)
            {
                int index = pathPoints.Count - 1;
                var nextPos = pathPoints[index];
                var direction = nextPos - transform.Position;
                var newRotation = Quaternion.LookRotation(direction, Vector3.up);
                newRotation.x = 0f;
                newRotation.z = 0f;
                transform.Rotation = Quaternion.Lerp(transform.Rotation, newRotation, angularSpeed);
                transform.Position = Vector3.MoveTowards(transform.Position, nextPos, speed * dt);
                if (Distance(transform.Position, nextPos) < 0.1f)
                {
                    pathPoints.RemoveAt(index);
                    index = pathPoints.Count - 1;
                    if (pathPoints.Count <= 0)
                        OnFinish?.Invoke();
                    else
                        OnNextPath?.Invoke(pathPoints[index]);
                }
            }
        }

        private float Distance(Vector3 a, Vector3 b)
        {
            var vector = new Vector3(a.x - b.x, 0f, a.z - b.z);
            return Mathf.Sqrt(vector.x * vector.x + vector.z * vector.z);
        }

        public bool SetDestination(Vector3 target)
        {
            System.GetPath(transform.Position, target, pathPoints, findPathMode, m_straightPathOptions);
            pathPoints.Reverse(); //反转是因为后面每一步会进行移除, 移除时数组不会进行倒塌操作
            if (pathPoints.Count <= 0)
                OnFinish?.Invoke();
            else
                OnNextPath?.Invoke(pathPoints[pathPoints.Count - 1]);
            return pathPoints.Count > 0;
        }

        public void OnDrawGizmos(Action<Vector3, Vector3> onDrawLine)
        {
            if (pathPoints.Count > 0)
            {
                int index = pathPoints.Count - 1;
                var nextPos = pathPoints[index];
                nextPos.y += agentHeight;
                onDrawLine(transform.Position, nextPos);
                for (int i = index; i > 0; i--)
                {
                    var start = pathPoints[i];
                    var end = pathPoints[i - 1];
                    start.y += agentHeight;
                    end.y += agentHeight;
                    onDrawLine(start, end);
                }
            }
        }

        public void Stop()
        {
            pathPoints.Clear();
        }
    }
}